package ar.edu.itba.it.cg.grupo01.impl.shape;

import java.awt.Color;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.vecmath.Point2f;
import javax.vecmath.Point3f;
import javax.vecmath.Vector3f;

import ar.edu.itba.it.cg.grupo01.api.Intersection;
import ar.edu.itba.it.cg.grupo01.api.Ray;
import ar.edu.itba.it.cg.grupo01.api.shape.Shape;
import ar.edu.itba.it.cg.grupo01.impl.IntersectionFactory;
import ar.edu.itba.it.cg.grupo01.impl.Precision;
import ar.edu.itba.it.cg.grupo01.impl.Utilities;

public class Rectangle extends AbstractShape {

	private Point3f p;
	private Point3f q;
	private Point3f r1;
	private Point3f r2;
	private Vector3f up = null;

	private CompositeShape rectangle;

	public Rectangle(final Point3f a, final Point3f b, final Point3f c,
			IntersectionFactory intersectionFactory) {
		this(a, b, c, intersectionFactory, null);
	}

	public Rectangle(final Point3f a, final Point3f b, final Point3f c,
			IntersectionFactory intersectionFactory, Color color) {
		super(intersectionFactory);

		List<Point3f> points = Utilities.pointsFormRightTriangle(a, b, c);

		if (points == null) {
			throw new RuntimeException(
					"Specified points do not describe a rectangle");
		}

		this.r1 = points.get(0);
		this.p = points.get(1);
		this.q = points.get(2);

		Vector3f v = new Vector3f(this.p);
		v.sub(this.r1);

		this.r2 = new Point3f(this.q);
		this.r2.add(v);

		Set<Triangle> triangles = new HashSet<Triangle>();
		Triangle t1 = new Triangle(this.p, this.q, this.r1, intersectionFactory);
		Triangle t2 = new Triangle(this.p, this.q, this.r2, intersectionFactory);
		triangles.add(t1);
		triangles.add(t2);
		setUpVector();

		this.rectangle = new CompositeShape(triangles, intersectionFactory);
	}

	@Override
	public Intersection<? extends Shape> intersects(Ray ray) {

		return this.rectangle.intersects(ray);
	}

	@Deprecated
	public Shape applyTransformation() {

		transformations.transform(this.p);
		transformations.transform(this.q);
		transformations.transform(this.r1);
		transformations.transform(this.r2);

		return new Rectangle(this.p, this.q, this.r1, intersectionFactory);
	}

	@Override
	public boolean containedInAABB(AABB box) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public Point3f getMaxXYZ() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Point3f getMinXYZ() {
		// TODO Auto-generated method stub
		return null;
	}

	private void setUpVector() {

		Set<Shape> triangles = rectangle.getMembers();

		for (Shape triangle : triangles) {
			this.up = ((Triangle) triangle).getContainingPlane().normal;
		}
	}

	public Point3f getCenter() {

		Vector3f a = new Vector3f(this.r1);
		Vector3f b = new Vector3f(this.r2);
		Vector3f c = new Vector3f(this.q);

		a.sub(this.p);
		b.sub(this.p);
		c.sub(this.p);

		Point3f center = new Point3f(this.p);
		a.scale(0.5f);
		b.scale(0.5f);
		c.scale(0.5f);
		if (Precision.epsilonEquals(a.dot(b), 0, Precision.EPSILON)) {
			center.add(a);
			center.add(b);
		} else if (Precision.epsilonEquals(a.dot(c), 0, Precision.EPSILON)) {
			center.add(a);
			center.add(c);
		} else if (Precision.epsilonEquals(b.dot(c), 0, Precision.EPSILON)) {
			center.add(b);
			center.add(c);
		}
		return center;
	}

	@Override
	public Collection<Point3f> getVertices() {

		List<Point3f> ret = new ArrayList<Point3f>();
		ret.add(this.p);
		ret.add(this.q);
		ret.add(this.r1);
		ret.add(this.r2);

		return ret;
	}

	@Override
	public Shape getTransformedShape() {

		return new Rectangle(this.p, this.q, this.r1, this.intersectionFactory);
	}

	@Override
	public Vector3f getNormal(Point3f point) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Point2f getUVCoordinates(Point3f point) {

		Vector3f uvec=new Vector3f(p);
		uvec.sub(point);

		return new Point2f(Math.abs(uvec.x),Math.abs(uvec.y));
		
	}

	@Override
	public Point3f getUVWCoordinates(Point3f point) {
		Point3f result = (Point3f) point.clone();
		result.sub(getCenter());
		return result;
	}
}
